/*
 * Copyright (C) 2018 Zhejiang lishiots Technology CO.,LTD.
 * All rights reserved.
 * Official Web Site: http://www.lishiots.com.
 * Developer Web Site: http://open.lishiots.com.
 */

package com.leonzx.base.config.coss;

import com.google.gson.*;
import com.google.gson.reflect.TypeToken;
import com.leonzx.base.config.coss.client.FileBytesRequest;
import com.leonzx.base.config.coss.client.FileBytesResponse;
import com.leonzx.base.config.coss.common.OSSClientMessage;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.io.FileUtils;
import org.apache.http.HttpStatus;
import org.apache.http.NameValuePair;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.StringEntity;
import org.apache.http.entity.mime.MultipartEntityBuilder;
import org.apache.http.entity.mime.content.FileBody;
import org.apache.http.entity.mime.content.StringBody;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.protocol.HTTP;
import org.apache.http.util.EntityUtils;

import java.io.File;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.List;

/***
 *
 * @since:oss-server 1.0
 * @author <a href="mailto:xiaoymin@foxmail.com">xiaoymin@foxmail.com</a> 
 * 2018/05/30 17:00
 */
public class OSSClient {

    /***
     * byte64 上传
     */
    private static final String v1_upload_file_binary_api = "/oss/material/uploadByBinary";

    /***
     * form表单上传
     */
    private static final String UPLOAD_FILE_FORM_API = "/oss/material/uploadMaterialNonProUrl";

    /**
     * 删除附件功能
     */
    private static final String DELETE_FILE_FORM_API = "/oss/material/delete";

    /**
     * 获取访问链接
     */
    private static final String DOWNLOAD_FILE_FORM_API = "/oss/material/download";


    private OSSClientProperty ossClientProperty;


    public OSSClient(OSSClientProperty ossClientProperty) {
        this.ossClientProperty = ossClientProperty;
        ossClientProperty.setRoot(ossClientProperty.getRemote());
        //初始化上传url地址
        if (ossClientProperty != null && ossClientProperty.getRemote() != null && !"".equalsIgnoreCase(ossClientProperty.getRemote())) {
            StringBuffer endpoint = new StringBuffer();
            endpoint.append(ossClientProperty.getRemote());
            if (ossClientProperty.getRemote().endsWith("/")) {
                endpoint.append(UPLOAD_FILE_FORM_API.substring(1));
            } else {
                endpoint.append(UPLOAD_FILE_FORM_API);
            }
            this.ossClientProperty.setRemote(endpoint.toString());
        }
    }

    public void changeEndpoint(String url) {
        StringBuffer endpoint = new StringBuffer();
        endpoint.append(ossClientProperty.getRoot());
        if (ossClientProperty.getRoot().endsWith("/")) {
            endpoint.append(url.substring(1));
        } else {
            endpoint.append(url);
        }
        this.ossClientProperty.setRemote(endpoint.toString());
    }

    /***
     * 设置默认header
     * @param request
     */
    private void addDefaultHeader(HttpUriRequest request) {
        request.addHeader("Content-Encoding", "gzip");
        request.addHeader("Content-type", "application/json");
    }

    private void handleServerExceptionMessage(OSSClientMessage ossClientMessage, Exception e) {
        ossClientMessage.setCode("8500");
        ossClientMessage.setMessage(e.getMessage());
    }

    /***
     * 创建数组
     * @param fileBytesRequests
     * @return
     */
    private JsonArray createFileArrs(List<FileBytesRequest> fileBytesRequests) {
        JsonArray jsonArray = new JsonArray();
        for (FileBytesRequest fileBytesRequest : fileBytesRequests) {
            JsonObject fileObj = new JsonObject();
            fileObj.addProperty("media_type", fileBytesRequest.getMediaType());
            fileObj.addProperty("file", fileBytesRequest.getFile());
            fileObj.addProperty("original_name", fileBytesRequest.getOriginalName());
            jsonArray.add(fileObj);
        }
        return jsonArray;
    }

    /**
     * 创建请求参数
     *
     * @param fileBytesRequests
     * @param project
     * @return
     */
    private JsonObject createRequestParams(List<FileBytesRequest> fileBytesRequests, String project) {
        JsonObject param = new JsonObject();
        param.addProperty("project", project);
        param.addProperty("appid", ossClientProperty.getAppid());
        param.addProperty("appsecret", ossClientProperty.getAppsecret());
        param.add("files", createFileArrs(fileBytesRequests));
        return param;
    }

    private JsonObject createRequestParams(String key) {
        JsonObject param = new JsonObject();
        param.addProperty("id", key);
        return param;
    }

    private void addRequestParam(HttpPost request, List<FileBytesRequest> fileBytesRequests, String project) {
        JsonObject param = createRequestParams(fileBytesRequests, ossClientProperty.getProject());
        request.setEntity(new StringEntity(param.toString(), "UTF-8"));
    }

    private void addRequestParam(HttpPost request, String key) throws UnsupportedEncodingException {
        List<NameValuePair> nvps = new ArrayList<>();
        nvps.add(new BasicNameValuePair("id", key));
        UrlEncodedFormEntity entity = new UrlEncodedFormEntity(nvps, "UTF-8");
        request.setEntity(entity);
    }


    /***
     * 处理结果
     * @param closeableHttpResponse
     * @param ossClientMessage
     * @param type
     * @throws IOException
     */
    private void handleResult(CloseableHttpResponse closeableHttpResponse, OSSClientMessage ossClientMessage, Type type) throws IOException {
        if (closeableHttpResponse.getStatusLine().getStatusCode() == HttpStatus.SC_OK) {
            String content = EntityUtils.toString(closeableHttpResponse.getEntity(), "UTF-8");
            if (content != null && !"".equals(content)) {
                JsonElement jsonE = new JsonParser().parse(content);
                ossClientMessage.setCode(jsonE.getAsJsonObject().get("code").getAsString());
                ossClientMessage.setMessage(jsonE.getAsJsonObject().get("message").getAsString());
                JsonElement data = jsonE.getAsJsonObject().get("data");
                ossClientMessage.setData(new Gson().fromJson(data, type));
            }
        }
    }

    /***
     * 文件上传
     * @param file
     * @return
     */
    public OSSClientMessage<FileBytesResponse> uploadFile(File file) {
        OSSClientMessage<FileBytesResponse> ossClientMessage = new OSSClientMessage<>();
        try {
            if (file.isDirectory()) {
                //文件不能是目录
                throw new RuntimeException("file can't be directory ");
            }
            //获取文件原始名称
            String originalName = file.getName();
            String mediaType = "unkown";
            int idx = originalName.lastIndexOf(".");
            if (idx > 0) {
                mediaType = originalName.substring(idx + 1);
            }
            String filebyteString = Base64.encodeBase64String(FileUtils.readFileToByteArray(file));
            FileBytesRequest fileBytesRequest = new FileBytesRequest();
            fileBytesRequest.setFile(filebyteString);
            fileBytesRequest.setMediaType(mediaType);
            fileBytesRequest.setOriginalName(originalName);
            ossClientMessage = uploadFileByte(fileBytesRequest);
        } catch (Exception e) {
            handleServerExceptionMessage(ossClientMessage, e);
        }
        return ossClientMessage;
    }

    /***
     * 批量上传文件
     * @param uploadFiles
     * @return
     */
    public OSSClientMessage<List<FileBytesResponse>> uploadFiles(List<File> uploadFiles) {
        OSSClientMessage<List<FileBytesResponse>> ossClientMessage = new OSSClientMessage<>();
        try {
            List<FileBytesRequest> fileBytesRequests = new ArrayList<>();
            for (File file : uploadFiles) {
                if (file.isDirectory()) {
                    //文件不能是目录
                    throw new RuntimeException("file {" + file.getName() + "} can't be directory ");
                }
            }
            for (File file : uploadFiles) {
                //获取文件原始名称
                String originalName = file.getName();
                String mediaType = "unkown";
                int idx = originalName.lastIndexOf(".");
                if (idx > 0) {
                    mediaType = originalName.substring(idx + 1);
                }
                String filebyteString = Base64.encodeBase64String(FileUtils.readFileToByteArray(file));
                FileBytesRequest fileBytesRequest = new FileBytesRequest();
                fileBytesRequest.setFile(filebyteString);
                fileBytesRequest.setMediaType(mediaType);
                fileBytesRequest.setOriginalName(originalName);
                fileBytesRequests.add(fileBytesRequest);
            }
            ossClientMessage = uploadFilesByte(fileBytesRequests);
        } catch (Exception e) {
            handleServerExceptionMessage(ossClientMessage, e);
        }
        return ossClientMessage;
    }

    /***
     * 字节字符串形式上传文件
     * @param fileBytesRequest
     * @return
     */
    public OSSClientMessage<FileBytesResponse> uploadFileByte(FileBytesRequest fileBytesRequest) {
        OSSClientMessage<FileBytesResponse> ossClientMessage = new OSSClientMessage<>();
        CloseableHttpResponse closeableHttpResponse = null;
        CloseableHttpClient httpClient = null;
        try {
            HttpPost request = new HttpPost(ossClientProperty.getRemote());
            addDefaultHeader(request);
            httpClient = HttpClients.createDefault();
            List<FileBytesRequest> fileBytesRequests = new ArrayList<>();
            fileBytesRequests.add(fileBytesRequest);
            addRequestParam(request, fileBytesRequests, ossClientProperty.getProject());
            closeableHttpResponse = httpClient.execute(request);
            ossClientMessage = getResult(closeableHttpResponse, ossClientMessage);
        } catch (Exception e) {
            handleServerExceptionMessage(ossClientMessage, e);
        }
        return ossClientMessage;
    }


    /***
     * form表单提交
     * @param file
     * @return
     */
    public OSSClientMessage<FileBytesResponse> uploadFileByForm(File file) {
        OSSClientMessage<FileBytesResponse> ossClientMessage = new OSSClientMessage<>();
        CloseableHttpResponse closeableHttpResponse = null;
        CloseableHttpClient httpClient = null;
        try {
            HttpPost request = new HttpPost(ossClientProperty.getRemote());
            httpClient = HttpClients.createDefault();
            MultipartEntityBuilder builder = MultipartEntityBuilder.create();
            changeEndpoint(UPLOAD_FILE_FORM_API);
            //表单参数
            builder.addPart("appid", new StringBody(ossClientProperty.getAppid(), ContentType.MULTIPART_FORM_DATA));
            builder.addPart("appsecret", new StringBody(ossClientProperty.getAppsecret(), ContentType.MULTIPART_FORM_DATA));
            builder.addPart("project", new StringBody(ossClientProperty.getProject(), ContentType.MULTIPART_FORM_DATA));
            //添加文件
            builder.addPart("file", new FileBody(file));
            //参数赋值
            request.setEntity(builder.build());
            closeableHttpResponse = httpClient.execute(request);
            ossClientMessage = getResult(closeableHttpResponse, ossClientMessage);
        } catch (Exception e) {
            handleServerExceptionMessage(ossClientMessage, e);
        }
        return ossClientMessage;
    }


    /**
     * 批量上传文件
     *
     * @param fileBytesRequests
     * @return
     */
    public OSSClientMessage<List<FileBytesResponse>> uploadFilesByte(List<FileBytesRequest> fileBytesRequests) {
        OSSClientMessage<List<FileBytesResponse>> ossClientMessage = new OSSClientMessage<>();
        CloseableHttpResponse closeableHttpResponse = null;
        CloseableHttpClient httpClient = null;
        try {
            HttpPost request = new HttpPost(ossClientProperty.getRemote());
            addDefaultHeader(request);
            httpClient = HttpClients.createDefault();
            addRequestParam(request, fileBytesRequests, ossClientProperty.getProject());
            closeableHttpResponse = httpClient.execute(request);
            if (closeableHttpResponse.getStatusLine().getStatusCode() == HttpStatus.SC_OK) {
                String content = EntityUtils.toString(closeableHttpResponse.getEntity(), "UTF-8");
                if (content != null && !"".equals(content)) {
                    JsonElement jsonE = new JsonParser().parse(content);
                    ossClientMessage.setCode(jsonE.getAsJsonObject().get("code").getAsString());
                    ossClientMessage.setMessage(jsonE.getAsJsonObject().get("message").getAsString());
                    Type type = new TypeToken<List<FileBytesResponse>>() {
                    }.getType();
                    List<FileBytesResponse> fileBytesResponse = new Gson().fromJson(jsonE.getAsJsonObject().get("data"), type);
                    if (fileBytesResponse != null && fileBytesResponse.size() > 0) {
                        ossClientMessage.setData(fileBytesResponse);
                    }
                }
            }
        } catch (Exception e) {
            handleServerExceptionMessage(ossClientMessage, e);
        }
        return ossClientMessage;
    }

    /**
     * @param key
     * @return
     */
    public OSSClientMessage<FileBytesResponse> delete(String key) {
        OSSClientMessage<FileBytesResponse> ossClientMessage = new OSSClientMessage<>();
        CloseableHttpResponse closeableHttpResponse = null;
        CloseableHttpClient httpClient = null;
        try {
            changeEndpoint(DELETE_FILE_FORM_API);
            HttpPost request = new HttpPost(ossClientProperty.getRemote());
            request.addHeader("Content-Encoding", "gzip");
            httpClient = HttpClients.createDefault();
            addRequestParam(request, key);
            closeableHttpResponse = httpClient.execute(request);
            ossClientMessage = getResult(closeableHttpResponse, ossClientMessage);
        } catch (Exception e) {
            handleServerExceptionMessage(ossClientMessage, e);
        }
        return ossClientMessage;
    }

    /**
     *
     *
     * @param key
     * @return
     */
    public OSSClientMessage<FileBytesResponse> downloadUrl(String key) {
        OSSClientMessage<FileBytesResponse> ossClientMessage = new OSSClientMessage<>();
        CloseableHttpResponse closeableHttpResponse = null;
        CloseableHttpClient httpClient = null;
        try {
            changeEndpoint(DOWNLOAD_FILE_FORM_API);
            HttpPost request = new HttpPost(ossClientProperty.getRemote());
            request.addHeader("Content-Encoding", "gzip");
            httpClient = HttpClients.createDefault();
            addRequestParam(request, key);
            closeableHttpResponse = httpClient.execute(request);
            ossClientMessage = getResult(closeableHttpResponse, ossClientMessage);
        } catch (Exception e) {
            handleServerExceptionMessage(ossClientMessage, e);
        }
        return ossClientMessage;
    }
    /**
     * @param closeableHttpResponse
     * @param ossClientMessage
     * @return
     * @throws IOException
     */
    public OSSClientMessage<FileBytesResponse> getResult(CloseableHttpResponse closeableHttpResponse, OSSClientMessage<FileBytesResponse> ossClientMessage) throws IOException {
        if (closeableHttpResponse.getStatusLine().getStatusCode() == HttpStatus.SC_OK) {
            String content = EntityUtils.toString(closeableHttpResponse.getEntity(), "UTF-8");
            if (content != null && !"".equals(content)) {
                JsonElement jsonE = new JsonParser().parse(content);
                ossClientMessage.setCode(jsonE.getAsJsonObject().get("code").getAsString());
                ossClientMessage.setMessage(jsonE.getAsJsonObject().get("message").getAsString());
                Type type = new TypeToken<List<FileBytesResponse>>() {
                }.getType();
                List<FileBytesResponse> fileBytesResponse = new Gson().fromJson(jsonE.getAsJsonObject().get("data"), type);
                if (fileBytesResponse != null && fileBytesResponse.size() > 0) {
                    ossClientMessage.setData(fileBytesResponse.get(0));
                }
            }
        }
        return ossClientMessage;
    }
}
